﻿
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
	using System.Globalization;
    using System.Linq;
    using System.Reflection;
	using System.Text;
	using System.Text.RegularExpressions;

#if WinRT
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
	using Windows.UI.Xaml.Markup;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Interactivity;
    using TriggerBase = Windows.UI.Interactivity.TriggerBase;
    using EventTrigger = Windows.UI.Interactivity.EventTrigger;
    using TriggerAction = Windows.UI.Interactivity.TriggerAction;
#else
	using System.Windows;
    using System.Windows.Data;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
	using System.Windows.Markup;
    using System.Windows.Media;
    using System.Windows.Interactivity;
    using EventTrigger = System.Windows.Interactivity.EventTrigger;
    using TriggerBase = System.Windows.Interactivity.TriggerBase;
    using TriggerAction = System.Windows.Interactivity.TriggerAction;
#endif


namespace DailyFx.UI 
{
	using DailyFx.Common;


    /// <summary>
    ///		Used to send a message from the UI to a presentation model class, 
	///		indicating that a particular Action should be invoked.
    /// </summary>
#if WinRT
    [ContentProperty(Name = "Parameters")]
#else
    [ContentProperty("Parameters")]
    [DefaultTrigger(typeof(FrameworkElement), typeof(EventTrigger), "MouseLeftButtonDown")]
    [DefaultTrigger(typeof(ButtonBase), typeof(EventTrigger), "Click")]
#endif
    [TypeConstraint(typeof(FrameworkElement))]
    public class MessageAction : TriggerAction<FrameworkElement> 
	{
        static readonly ILog Log = LogManager.GetLog(typeof(MessageAction));
        
		/// <summary>
		///		the weak action context for this message action .
		/// </summary>
		WeakAction _context;


		/// <summary>
		///		Represents the handler delegation of an action message .
		/// </summary>
        internal static readonly DependencyProperty HandlerProperty = DependencyProperty.RegisterAttached
			("Handler", typeof(object), typeof(MessageAction), new PropertyMetadata(null, OnHandlerChanged));

		/// <summary>
		/// 
		/// </summary>
		static void OnHandlerChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e )
		{
			((MessageAction)d).NotifyContextChanged();
		}


		/// <summary>
		///		Represents the method name of an action message.
		/// </summary>
		public static readonly DependencyProperty MethodProperty = DependencyProperty.Register
			( "Method", typeof(string), typeof(MessageAction), null );

		/// <summary>
		/// Represents the parameters of an action message.
		/// </summary>
		public static readonly DependencyProperty ParametersProperty = DependencyProperty.Register
			("Parameters", typeof(AttachedCollection<Parameter>), typeof(MessageAction), null);


        ///<summary>
        ///		Causes the action invocation to "double check" 
		///		if the action should be invoked by executing the guard immediately before hand.
        ///</summary>
        /// <remarks>
		///		This is disabled by default. If multiple actions are attached to the same element, 
		///		you may want to enable this so that each individaul action checks its guard 
		///		regardless of how the UI state appears.
		///	</remarks>
        public static bool EnforceGuardsDuringInvocation = false;

        ///<summary>
        /// Causes the action to throw if it cannot locate the target or the method at invocation time.
        ///</summary>
        /// <remarks>True by default.</remarks>
        public static bool ThrowsExceptions = true;

        
        /// <summary>
        ///		Creates an instance of <see cref="MessageAction"/>.
        /// </summary>
        public MessageAction ()
		{
            SetValue( ParametersProperty, new AttachedCollection<Parameter>());
        }

        /// <summary>
        ///		Gets or sets the name of the method to be invoked on the presentation model class.
        /// </summary>
#if !WinRT
        [Category("Common Properties")]
#endif
        public string Method 
		{
            get { return (string)GetValue(MethodProperty); }
            set { SetValue(MethodProperty, value); }
        }

        /// <summary>
        ///		Gets the parameters to pass as part of the method invocation.
        /// </summary>
#if WinRT
        public AttachedCollection<Parameter> Parameters 
		{
            get { return (AttachedCollection<Parameter>)GetValue(ParametersProperty); }
        }
#else
        [Category("Common Properties")]
        public AttachedCollection<Parameter> Parameters 
		{
            get { return (AttachedCollection<Parameter>)GetValue(ParametersProperty); }
        }
#endif

		/// <summary>
		/// 
		/// </summary>
		void OnElementLoaded ( object sender, RoutedEventArgs e)
		{
			this.NotifyContextChanged ();

			DependencyObject currentElement;
			if (_context.View == null)
			{
				currentElement = AssociatedObject;
				while (currentElement != null)
				{
					if ( ActionProps.HasTarget( currentElement) )
						break;

					currentElement = VisualTreeHelper.GetParent(currentElement);
				}
			}
			else currentElement = _context.View;

#if NET
			var binding = new Binding
			{
				Path = new PropertyPath( MsgProps.HandlerProperty ),
				Source = currentElement
			};
#elif WinRT
            var binding = new Binding 
			{
                Source = currentElement
            };
#else
            const string bindingText = "<Binding xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation\' " +
												"xmlns:ui='clr-namespace:DailyFx.UI;assembly=DailyFx.UI' " +
												"Path='(ui:MsgProps.Handler)' />";

            var binding = (Binding)XamlReader.Load( bindingText);
            binding.Source = currentElement;
#endif
			BindingOperations.SetBinding( this, HandlerProperty, binding );

		}

      
        /// <summary>
        ///		Called after the action is attached to an AssociatedObject.
        /// </summary>
        protected override void OnAttached ()
		{
            if ( !Designer.IfTrue ) 
			{
                this.Parameters.Attach(AssociatedObject);
				this.Parameters.Apply( x => x.MakeAwareOf(this) ) ;

                if ( View.CarryOnViewLoaded( this.AssociatedObject, this.OnElementLoaded) )
				{
                    var trigger = Interaction.GetTriggers(AssociatedObject)
                        .FirstOrDefault(t => t.Actions.Contains(this)) as EventTrigger;

                    if ( trigger != null && trigger.EventName == "Loaded" )
                        Invoke( new RoutedEventArgs() );
                }
            }

            base.OnAttached ();
        }

		/// <summary>
		///		Occurs before the message detaches from the associated object.
		/// </summary>
		public event EventHandler Detaching = delegate { };

        /// <summary>
        ///		Called when the action is being detached from its AssociatedObject, 
		///		but before it has actually occurred.
        /// </summary>
        protected override void OnDetaching ( ) 
		{
            if ( !Designer.IfTrue ) 
			{
                this.Detaching(this, EventArgs.Empty);
                
				this.AssociatedObject.Loaded -= OnElementLoaded;
				this.Parameters.Detach();
            }

            base.OnDetaching();
        }


        /// <summary>
        ///		Invokes the action.
        /// </summary>
        protected override void Invoke ( object eventArgs ) 
		{
            Log.Info("Invoking {0}.", this);

            if ( this._context == null ) 
			{
                NotifyContextChanged();
            }

            if (_context.Target == null || _context.View == null)
			{
                Build(_context);
                if (_context.Target == null) 
				{
                    var ex = new Exception(string.Format("No target found for method {0}.", _context.MessageAction.Method));
                    Log.Error(ex);

                    if (!ThrowsExceptions)
                        return;
                    throw ex;
                }

				if ( !ApplyAvailabilityEffect(_context) )
				{
					Log.Info("{0} availability update.", this );
                    return;
                }
            }

            if ( _context.Method == null ) 
			{
                var ex = new Exception ( string.Format("Method {0} not found on target of type {1}.",
					_context.MessageAction.Method, _context.Target.GetType()) );
                Log.Error(ex);

                if (!ThrowsExceptions)
                    return;
                throw ex;
            }

            this._context.EventArgs = eventArgs;

            if ( EnforceGuardsDuringInvocation && 
				this._context.CanExecute != null && !this._context.CanExecute() )
			{
                return;
            }

            InvokeAction(_context);
            _context.EventArgs = null;
        }

		/// <summary>
		///		Invokes the action using the specified <see cref="WeakAction"/>
		/// </summary>
		public static Action<WeakAction> InvokeAction = context =>
		{
			var values = TryResolveParameters( context, context.Method.GetParameters() );
			var retVal = context.Method.Invoke( context.Target, values);


			var cmd = retVal as ICommand;
			if ( cmd != null)
			{
				retVal = new[] { cmd };
			}

			var cmds = retVal as IEnumerable<ICommand>;
			if ( cmds != null)
			{
				retVal = cmds.GetEnumerator();
			}

			var c = retVal as IEnumerator<ICommand>;
			if ( c != null )
			{
				CoroutineSystem.Call ( c, context );
			}
		};


		/// <summary>
		///		The special parameter values recognized by the message binder along with their resolvers.
		/// </summary>
		public static readonly Dictionary<string, Func<WeakAction, object>> Keywords =
			new Dictionary<string, Func<WeakAction, object>>
			{
                { "$eventargs", c => c.EventArgs },
                { "$datacontext", c => c.Source.DataContext },
                { "$source", c => c.Source },
                { "$executioncontext", c => c },
                { "$view", c => c.View }
            };


		/// <summary>
        ///		Custom converters used by the framework registered by destination type for which they will be selected.
        ///		The converter is passed the existing value to convert and a "context" object.
        /// </summary>
        public static readonly Dictionary<Type, Func<object, object, object>> Converters =
            new Dictionary<Type, Func<object, object, object>>
			{
                {
                    typeof(DateTime), (value, context) =>
					{
                        DateTime result;
                        DateTime.TryParse(value.ToString(), out result);
                        return result;
                    }
                }
            };


		/// <summary>
		///		Determines the parameters that a method should be invoked with.
		/// </summary>
		static object[] TryResolveParameters ( WeakAction weakAction, ParameterInfo[] requiredParameters )
		{
			var originalValues = weakAction.MessageAction.Parameters.Select( x => x.Value ).ToArray();
			var actualValues = new object[ requiredParameters.Length ];

			for ( int i = 0; i < requiredParameters.Length; i++ )
			{
				var paramType = requiredParameters[i].ParameterType;
				var paramValue = originalValues[i];

				var paramAsText = paramValue as string;

				if ( paramAsText != null )
				{
#if WinRT
					var lookup = paramAsText.ToLower();
#else
					var lookup = paramAsText.ToLower( CultureInfo.InvariantCulture );
#endif
					// transforms the textual parameter into the actual parameter.
					Func<WeakAction, object> resolver;
					object paramValue2 = Keywords.TryGetValue(lookup, out resolver) ? resolver(weakAction) : paramAsText ;

					actualValues[i] = CoercePrameterObject(paramType, paramValue2, weakAction);
				}
					
				else 
				{
					actualValues[i] = CoercePrameterObject(paramType, paramValue, weakAction);
				}
			}

			return actualValues;
		}


		/// <summary>
		///		Coerces the provided value to the destination type.
		/// </summary>
		static object CoercePrameterObject ( Type targetType, object providedValue, object context )
		{
			if (providedValue == null)
			{
				return TypeExtension.GetDefaultIfPod ( targetType );
			}

			var providedType = providedValue.GetType();
			if ( targetType.IsAssignableFrom(providedType) )
			{
				return providedValue;
			}

			if ( Converters.ContainsKey(targetType) )
			{
				return Converters[targetType]( providedValue, context );
			}

			try
			{
#if !WinRT
				var converter = TypeDescriptor.GetConverter( targetType );
				if ( converter.CanConvertFrom(providedType) )
				{
					return converter.ConvertFrom(providedValue);
				}

				converter = TypeDescriptor.GetConverter(providedType);
				if ( converter.CanConvertTo(targetType) )
				{
					return converter.ConvertTo( providedValue, targetType );
				}
#endif
#if WinRT
                if ( destinationType.GetTypeInfo().IsEnum )
                {
#else
				if ( targetType.IsEnum )
				{
#endif
					var stringValue = providedValue as string;
					if (stringValue != null)
					{
						return Enum.Parse(targetType, stringValue, true);
					}

					return Enum.ToObject( targetType, providedValue );
				}

				if ( typeof(Guid).IsAssignableFrom(targetType) )
				{
					var stringValue = providedValue as string;
					if ( stringValue != null )
					{
						return new Guid(stringValue);
					}
				}
			}
			catch
			{
				return TypeExtension.GetDefaultIfPod ( targetType );
			}

			try
			{
				return Convert.ChangeType( providedValue, targetType, CultureInfo.CurrentUICulture );
			}
			catch
			{
				return TypeExtension.GetDefaultIfPod( targetType );
			}
		}


		void NotifyContextChanged ()
		{
			if (this._context != null)
				this._context.Dispose();

			this._context = new WeakAction
			{
				MessageAction = this,
				Source = AssociatedObject
			};

			this.NotifyAvailabilityChanged ();
		}


		/// <summary>
		///		Forces an update of the UI's Enabled/Disabled state based on 
		///		the the preconditions associated with the method .
		/// </summary>
		public void NotifyAvailabilityChanged ()
		{
			if (this._context == null)
				return;

			if (this._context.Target == null || this._context.View == null)
				Build( this._context);

			Log.Info("{0} availability update.", this);
			ApplyAvailabilityEffect( this._context);
		}

		/// <summary>
		///		Applies an availability effect, such as IsEnabled, to an element.
		/// </summary>
		public static Func<WeakAction, bool> ApplyAvailabilityEffect = context =>
		{
#if WINDOWS_PHONE
            if (context.Message.applicationBarSource != null) 
			{
                if(context.CanExecute != null)
                    context.Message.applicationBarSource.IsEnabled = context.CanExecute();
                return context.Message.applicationBarSource.IsEnabled;
            }
#endif

#if SILVERLIGHT || WinRT
            if (!(context.Source is Control) )
			{
                return true;
            }
#endif

#if SILVERLIGHT || WinRT
            var source = (Control)context.Source;
            if (ConventionManager.HasBinding(source, Control.IsEnabledProperty))
			{
                return source.IsEnabled;
            }
#else
			var source = context.Source;
			if (ConventionManager.HasBinding( source, UIElement.IsEnabledProperty) )
			{
				return source.IsEnabled;
			}
#endif
			if (context.CanExecute != null)
			{
				source.IsEnabled = context.CanExecute();
			}

			return source.IsEnabled;
		};


		/// <summary>
		///		Prepares the action execution context for use.
		/// </summary>
		public static Action<WeakAction> Build = context =>
		{
			SetMethodBinding(context);
			if (context.Target == null || context.Method == null)
			{
				return;
			}

			var guardName = "Can" + context.Method.Name;
			var targetType = context.Target.GetType();
			var guard = TryFindGuardMethod( context );

			if ( guard == null )
			{
				var inpc = context.Target as INotifyPropertyChanged;
				if (inpc == null)
					return;
#if WinRT
                guard = targetType.GetRuntimeMethods().SingleOrDefault(m => m.Name == "get_" + guardName);
#else
				guard = targetType.GetMethod( "get_" + guardName );
#endif
				if (guard == null)
					return;

				PropertyChangedEventHandler handler = null;
				handler = (s, e) =>
				{
					if (string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == guardName)
					{
						Execution.OnUIThread(() =>
						{
							var message = context.MessageAction;
							if (message == null)
							{
								inpc.PropertyChanged -= handler;
								return;
							}
							message.NotifyAvailabilityChanged();
						});
					}
				};

				inpc.PropertyChanged += handler;
				context.Disposing += delegate { inpc.PropertyChanged -= handler; };
				context.MessageAction.Detaching += delegate { inpc.PropertyChanged -= handler; };
			}

			context.CanExecute = () => 
				(bool)guard.Invoke( context.Target, TryResolveParameters(context, guard.GetParameters()) );
		};


    
     
        /// <summary>
        ///		Finds the method on the target matching the specified message.
        /// </summary>
        public static Func<MessageAction, object, MethodInfo> GetTargetMethod = (message, target) =>
		{
#if WinRT
            return ( from method in target.GetType().GetRuntimeMethods()
								where method.Name == message.Method
								let methodParameters = method.GetParameters() 
								where message.Parameters.Count == methodParameters.Length
								select method
				).FirstOrDefault() ;
#else
            return (from method in target.GetType().GetMethods()
								where method.Name == message.Method
								let methodParameters = method.GetParameters()
								where message.Parameters.Count == methodParameters.Length
								select method
				).FirstOrDefault();
#endif
        };

        /// <summary>
        ///		Sets the target, method and view on the context. Uses a bubbling strategy by default.
        /// </summary>
        public static Action<WeakAction> SetMethodBinding = context =>
		{
            var source = context.Source;

            DependencyObject currentElement = source;
            while ( currentElement != null) 
			{
                if ( ActionProps.HasTarget(currentElement))
				{
                    var target = MsgProps.GetHandler(currentElement);
                    if (target != null) 
					{
                        var method = GetTargetMethod(context.MessageAction, target);
                        if (method != null) 
						{
                            context.Method = method;
                            context.Target = target;
                            context.View = currentElement;
                            return;
                        }
                    }
                    else 
					{
                        context.View = currentElement;
                        return;
                    }
                }

                currentElement = VisualTreeHelper.GetParent(currentElement);
            }

            if ( source != null && source.DataContext != null)
			{
                var target = source.DataContext;
                var method = GetTargetMethod(context.MessageAction, target);

                if (method != null) 
				{
                    context.Target = target;
                    context.Method = method;
                    context.View = source;
                }
            }
        };

   
        /// <summary>
        ///		Try to find a candidate for guard function, having:
        ///			- a name in the form "CanXXX"
        ///			- no generic parameters
        ///			- a bool return type
        ///			- no parameters or a set of parameters corresponding to the action method
        /// </summary>
        static MethodInfo TryFindGuardMethod ( WeakAction context)
		{
			var guardName = "Can" + context.Method.Name;
            var targetType = context.Target.GetType();
#if WinRT
            var guard = targetType.GetRuntimeMethods().SingleOrDefault(m => m.Name == guardName);
#else
			var guard = targetType.GetMethod(guardName);
#endif
			if (guard ==null) return null;
			if (guard.ContainsGenericParameters) return null;
			if (typeof(bool) != guard.ReturnType) return null;

			var guardParams = guard.GetParameters();
			var actionParams = context.Method.GetParameters();
			if (guardParams.Length == 0) return guard;
			if (guardParams.Length != actionParams.Length) return null;
#if WinRT
			var zipped = guardParams.Zip(context.Method.GetParameters(),
											(x, y) => x.ParameterType.Equals(y.ParameterType) );
#else
		    var zipped = guardParams.Zip( context.Method.GetParameters(),
											(x, y) => x.ParameterType == y.ParameterType );
#endif
			if ( zipped.Any(x => !x) )
			    return null;

			return guard;

        }


		static readonly Regex LongFormatRegularExpression =
			new Regex ( @"^[\s]*\[[^\]]*\][\s]*=[\s]*\[[^\]]*\][\s]*$" );


		/// <summary>
		///		Parses the specified message text.
		/// </summary>
		public static IEnumerable<TriggerBase> TryParseTriggerAndMessageAction ( DependencyObject target, string text )
		{
			if ( string.IsNullOrEmpty(text) )
			{
				return new TriggerBase[0];
			}

			var triggers = new List<TriggerBase>();
			var szMsgs = Split(text, ';');

			foreach (var msg in szMsgs )
			{
				var triggerPlusMessage = LongFormatRegularExpression.IsMatch(msg)
											 ? Split( msg, '=' )
											 : new[] { null, msg };

				var messageDetail = triggerPlusMessage.Last()
													.Replace("[", string.Empty)
													.Replace("]", string.Empty)
													.Trim();

				var trigger = CreateTrigger(target, triggerPlusMessage.Length == 1 ? null : triggerPlusMessage[0]);
				var message = CreateMessageAction (target, messageDetail);

				trigger.Actions.Add(message);
				triggers.Add(trigger);
			}

			return triggers;
		}

		static string[] Split ( string message, char separator )
		{
			//	Splits a string using the specified separator, 
			//  if it is found outside of relevant places delimited by [ and ]
			
			var parts = new List<string>();
			var builder = new StringBuilder();

			int brackets = 0;

			string str;
#if WinRT
            foreach ( var c in message.ToCharArray() )
            {
#else
			foreach ( var c in message )
			{
#endif
				//Square brackets are used as delimiters, so only separators outside them count...
				if (c == '[')
					brackets++;
				else if (c == ']')
					brackets--;

				else if ( c == separator )
				{
					if ( brackets == 0 )
					{
						str = builder.ToString();
						if ( !string.IsNullOrEmpty(str) )
						{
							parts.Add(builder.ToString());
						}
						
						builder.Length = 0;
						continue;
					}
				}
				builder.Append(c);
			}

			str = builder.ToString();
			if ( !string.IsNullOrEmpty(str) )
			{
				parts.Add( builder.ToString() );
			}

			return parts.ToArray();
		}


        
		/// <summary>
		///		Creates a trigger from xaml text .
		/// </summary>
		public static Func<DependencyObject, string, TriggerBase> CreateTrigger = ( target, text) =>
		{
			if ( text == null )
			{
				var defaults = ConventionManager.GetConvention( target.GetType() );
				return defaults.CreateTrigger();
			}

			var triggerDetail = text.Replace("[", string.Empty)
									.Replace("]", string.Empty)
									.Replace("Event", string.Empty)
									.Trim();

			return new EventTrigger { EventName = triggerDetail };
		};

		/// <summary>
		///		Creates an instance of <see cref="MessageAction"/> by parsing out the xml text descriiption .
		/// </summary>
		public static TriggerAction CreateMessageAction (DependencyObject target, string text)
		{
			var opening = text.IndexOf('(');
			if (opening < 0)
			{
				opening = text.Length;
			}

			var closing = text.LastIndexOf(')');
			if (closing < 0 )
			{
				closing = text.Length;
			}

			var core = text.Substring(0, opening).Trim();
			var message = new MessageAction { Method = Regex.Replace(core, "^Action", string.Empty).Trim() };

			if ( closing - opening > 1)
			{
				var paramString = text.Substring(opening + 1, closing - opening - 1);
					var parameters = ReadParameters( paramString );

					foreach (var parameter in parameters)
						message.Parameters.Add( CreateParameter(target, parameter.Trim()) );
			}

			return message;
		}

		static string[] ReadParameters ( string parameters )
        {
            // Splits parameter string taking into account brackets...
            var sections = new List<string>();
            var builder = new StringBuilder();

            bool inner = false;
            int curlyBrackets = 0, squareBrackets = 0, roundBrackets = 0;

            for (int i = 0; i < parameters.Length; i++)
            {
                var c = parameters[i];

                if (c == '"')
                {
                    if (i == 0 || parameters[i - 1] != '\\')
                    {
                        inner = !inner;
                    }
                }

                if ( !inner )
                {
                    switch (c)
                    {
                        case '{':	curlyBrackets++; break;
                        case '}':	curlyBrackets--; break;
                        case '[':	squareBrackets++; break;
                        case ']':	squareBrackets--; break;
                        case '(':	roundBrackets++; break;
                        case ')':	roundBrackets--; break;

                        default:
                            if ( c == ',' && 
								roundBrackets == 0 && squareBrackets == 0 && curlyBrackets == 0 )
                            {
                                // The only commas to be considered as parameter separators are outside:
                                //	- Strings /
                                //	- Square brackets (to ignore indexers)
                                //	- Parantheses (to ignore method invocations)
                                //	- Curly brackets (to ignore initializers and Bindings)
                                sections.Add( builder.ToString() );
                                builder.Length = 0;

                                continue;
                            }
                            break;
                    }
                }
                builder.Append( c );
            }
			// last part .
            sections.Add( builder.ToString() );
            
			// ok, returns all parameter sections .
			return sections.ToArray();
        }

		
		public static Func<DependencyObject, string, Parameter> CreateParameter = (target, text) =>
		{
			var actualParameter = new Parameter();

			if (text.StartsWith("'") && text.EndsWith("'"))
			{
				actualParameter.Value = text.Substring(1, text.Length - 2);
			}
			else if (MessageAction.Keywords.ContainsKey(text.ToLower()) || char.IsNumber(text[0]) )
			{
				actualParameter.Value = text;
			}
			else if (target is FrameworkElement)
			{
				var frameworkElement = (FrameworkElement) target;
				
				var nameWithMode = text.Split(':').Select(x => x.Trim()).ToArray();
				var index = nameWithMode[0].IndexOf('.');

				View.CarryOnViewLoaded ( frameworkElement, 
				delegate
				{
					BindParameter ( frameworkElement, actualParameter,
									index == -1 ? nameWithMode[0] : nameWithMode[0].Substring(0, index),
									index == -1 ? null : nameWithMode[0].Substring(index + 1),
									nameWithMode.Length == 2 ? (BindingMode)Enum.Parse(typeof(BindingMode), nameWithMode[1], true) : BindingMode.OneWay
								);
				});
			}

			return actualParameter;
		};


		/// <summary>
		///		Creates a binding on a <see cref="Parameter"/>.
		/// </summary>
		public static void BindParameter ( FrameworkElement target, Parameter parameter, string szChild, string path, BindingMode mode )
		{
			var element = szChild == "$this"
							? target
							: TreeHelper.FindNamedElement(TreeHelper.GetNamedChildren(target), szChild ) ;
			if ( element == null )
			{
				return;
			}

			if ( string.IsNullOrEmpty(path) )
			{
				path = ConventionManager.GetConvention(element.GetType()).ParameterProperty;
			}
#if WinRT
            var binding = new Binding
            {
                Path = new PropertyPath(path),
                Source = element	,
                Mode = mode			,
            };
#else
			var binding = new Binding(path)
			{
				Source = element,
				Mode = mode		,
			};
#endif

#if (SILVERLIGHT && !SL5)
            var expression = (BindingExpression)BindingOperations.SetBinding (
													parameter, Parameter.ValueProperty, binding );

            var field = element.GetType().GetField( path + "Property",
										BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy );
            if (field == null) 
			{
                return;
            }

			// todo: silverlight got no update source trigger .
            ConventionManager.ApplySilverlightTriggers ( element, (DependencyProperty)field.GetValue(null), x => expression, null, null );
#else
#	if !WinRT
			binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
#	endif
			BindingOperations.SetBinding ( parameter, Parameter.ValueProperty, binding );
#endif
		}

		/// <summary>
		///		Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </summary>
		public override string ToString()
		{
			return "Action: " + this.Method;
		}

   
    }
}
